using CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using Xunit;

namespace CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests.BindablePropertyAttributeSourceGeneratorTests;

public class IntegrationTests : BaseBindablePropertyAttributeSourceGeneratorTest
{
	[Fact]
	public async Task GenerateBindableProperty_ComplexInheritanceScenario_GeneratesCorrectCode()
	{
		const string source =
			/* language=C#-test */
			//lang=csharp
			$$"""
			using CommunityToolkit.Maui;
			using Microsoft.Maui.Controls;

			namespace {{defaultTestNamespace}};

			public abstract partial class BaseView : View
			{
			    [BindableProperty]
			    public partial string BaseText { get; set; }
			}

			public partial class DerivedView : BaseView
			{
			    [BindableProperty]
			    public partial string DerivedText { get; set; }

			    [BindableProperty]
			    public new partial string BaseText { get; set; }
			}
			""";

		const string expectedBaseGenerated =
			/* language=C#-test */
			//lang=csharp
			$$"""
			// <auto-generated>
			// See: CommunityToolkit.Maui.SourceGenerators.Internal.BindablePropertyAttributeSourceGenerator
			#pragma warning disable
			#nullable enable
			namespace {{defaultTestNamespace}};
			public partial class BaseView
			{
			    /// <summary>
			    /// Backing BindableProperty for the <see cref = "BaseText"/> property.
			    /// </summary>
			    public static readonly global::Microsoft.Maui.Controls.BindableProperty BaseTextProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("BaseText", typeof(string), typeof(TestNamespace.BaseView), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
			    public partial string BaseText { get => (string)GetValue(BaseTextProperty); set => SetValue(BaseTextProperty, value); }
			}
			""";

		const string expectedDerivedGenerated =
			/* language=C#-test */
			//lang=csharp
			$$"""
			// <auto-generated>
			// See: CommunityToolkit.Maui.SourceGenerators.Internal.BindablePropertyAttributeSourceGenerator
			#pragma warning disable
			#nullable enable
			namespace {{defaultTestNamespace}};
			public partial class DerivedView
			{
			    /// <summary>
			    /// Backing BindableProperty for the <see cref = "DerivedText"/> property.
			    /// </summary>
			    public static readonly global::Microsoft.Maui.Controls.BindableProperty DerivedTextProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("DerivedText", typeof(string), typeof(TestNamespace.DerivedView), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
			    public partial string DerivedText { get => (string)GetValue(DerivedTextProperty); set => SetValue(DerivedTextProperty, value); }

			    /// <summary>
			    /// Backing BindableProperty for the <see cref = "BaseText"/> property.
			    /// </summary>
			    public new static readonly global::Microsoft.Maui.Controls.BindableProperty BaseTextProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("BaseText", typeof(string), typeof(TestNamespace.DerivedView), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
			    public new partial string BaseText { get => (string)GetValue(BaseTextProperty); set => SetValue(BaseTextProperty, value); }
			}
			""";

		await VerifySourceGeneratorAsync(source, expectedAttribute, ("BaseView.g.cs", expectedBaseGenerated), ("DerivedView.g.cs", expectedDerivedGenerated));
	}

	[Fact]
	public async Task GenerateBindableProperty_GenericClass_GeneratesCorrectCode()
	{
		const string source =
			/* language=C#-test */
			//lang=csharp
			$$"""
		    using CommunityToolkit.Maui;
		    using Microsoft.Maui.Controls;

		    namespace {{defaultTestNamespace}};

		    public partial class {{defaultTestClassName}}<T,U> : View where T : class
		    {
		        [BindableProperty]
		        public partial T? Value { get; set; }

		        [BindableProperty]
		        public partial U? Name { get; set; }
		    }
		    """;

		const string expectedGenerated =
			/* language=C#-test */
			//lang=csharp
			$$"""
		    // <auto-generated>
		    // See: CommunityToolkit.Maui.SourceGenerators.Internal.BindablePropertyAttributeSourceGenerator
		    #pragma warning disable
		    #nullable enable
		    namespace {{defaultTestNamespace}};
		    public partial class {{defaultTestClassName}}<T, U>
		    {
		        /// <summary>
		        /// Backing BindableProperty for the <see cref = "Value"/> property.
		        /// </summary>
		        public static readonly global::Microsoft.Maui.Controls.BindableProperty ValueProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Value", typeof(T), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}<T, U>), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
		        public partial T? Value { get => (T? )GetValue(ValueProperty); set => SetValue(ValueProperty, value); }

		        /// <summary>
		        /// Backing BindableProperty for the <see cref = "Name"/> property.
		        /// </summary>
		        public static readonly global::Microsoft.Maui.Controls.BindableProperty NameProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Name", typeof(U), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}<T, U>), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
		        public partial U? Name { get => (U? )GetValue(NameProperty); set => SetValue(NameProperty, value); }
		    }
		    """;

		await VerifySourceGeneratorAsync(source, expectedGenerated);
	}

	[Fact]
	public async Task GenerateBindableProperty_NestedClass_GeneratesCorrectCode()
	{
		const string outerClassName = "Outerclass";

		const string source =
			/* language=C#-test */
			//lang=csharp
			$$"""
		    using CommunityToolkit.Maui;
		    using Microsoft.Maui.Controls;

		    namespace {{defaultTestNamespace}};

		    public partial class {{outerClassName}}
		    {
		        public partial class {{defaultTestClassName}} : View
		        {
		            [BindableProperty]
		            public partial string Text { get; set; }
		        }
		    }
		    """;

		const string expectedGenerated =
/* language=C#-test */
//lang=csharp
$$"""
// <auto-generated>
// See: CommunityToolkit.Maui.SourceGenerators.Internal.BindablePropertyAttributeSourceGenerator
#pragma warning disable
#nullable enable
namespace {{defaultTestNamespace}};
public partial class {{outerClassName}}
{
    public partial class {{defaultTestClassName}}
    {
        /// <summary>
        /// Backing BindableProperty for the <see cref = "Text"/> property.
        /// </summary>
        public static readonly global::Microsoft.Maui.Controls.BindableProperty TextProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Text", typeof(string), typeof({{defaultTestNamespace}}.{{outerClassName}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
        public partial string Text { get => (string)GetValue(TextProperty); set => SetValue(TextProperty, value); }
    }
}
""";

		await VerifySourceGeneratorAsync(source, expectedAttribute, ($"{outerClassName}.{defaultTestClassName}.g.cs", expectedGenerated));
	}

	[Fact]
	public async Task GenerateBindableProperty_WithCustomTypes_GeneratesCorrectCode()
	{
		const string source =
			/* language=C#-test */
			//lang=csharp
			$$"""
		    using CommunityToolkit.Maui;
		    using Microsoft.Maui.Controls;
		    using System.Collections.Generic;

		    namespace {{defaultTestNamespace}};

		    public class CustomModel
		    {
		        public string Name { get; set; }
		    }

		    public partial class {{defaultTestClassName}} : View
		    {
		        [BindableProperty]
		        public partial CustomModel Model { get; set; }

		        [BindableProperty]
		        public partial List<string> Items { get; set; }

		        [BindableProperty]
		        public partial Dictionary<string, object> Properties { get; set; }
		    }
		    """;

		const string expectedGenerated =
			/* language=C#-test */
			//lang=csharp
			$$"""
		    // <auto-generated>
		    // See: CommunityToolkit.Maui.SourceGenerators.Internal.BindablePropertyAttributeSourceGenerator
		    #pragma warning disable
		    #nullable enable
		    namespace {{defaultTestNamespace}};
		    public partial class {{defaultTestClassName}}
		    {
		        /// <summary>
		        /// Backing BindableProperty for the <see cref = "Model"/> property.
		        /// </summary>
		        public static readonly global::Microsoft.Maui.Controls.BindableProperty ModelProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Model", typeof(TestNamespace.CustomModel), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
		        public partial {{defaultTestNamespace}}.CustomModel Model { get => ({{defaultTestNamespace}}.CustomModel)GetValue(ModelProperty); set => SetValue(ModelProperty, value); }

		        /// <summary>
		        /// Backing BindableProperty for the <see cref = "Items"/> property.
		        /// </summary>
		        public static readonly global::Microsoft.Maui.Controls.BindableProperty ItemsProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Items", typeof(System.Collections.Generic.List<string>), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
		        public partial System.Collections.Generic.List<string> Items { get => (System.Collections.Generic.List<string>)GetValue(ItemsProperty); set => SetValue(ItemsProperty, value); }

		        /// <summary>
		        /// Backing BindableProperty for the <see cref = "Properties"/> property.
		        /// </summary>
		        public static readonly global::Microsoft.Maui.Controls.BindableProperty PropertiesProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Properties", typeof(System.Collections.Generic.Dictionary<string, object>), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
		        public partial System.Collections.Generic.Dictionary<string, object> Properties { get => (System.Collections.Generic.Dictionary<string, object>)GetValue(PropertiesProperty); set => SetValue(PropertiesProperty, value); }
		    }
		    """;

		await VerifySourceGeneratorAsync(source, expectedGenerated);
	}

	[Fact]
	public async Task GenerateBindableProperty_MultipleClassesInSameFile_GeneratesCorrectCode()
	{
		const string source =
			/* language=C#-test */
			//lang=csharp
			$$"""
		    using CommunityToolkit.Maui;
		    using Microsoft.Maui.Controls;

		    namespace {{defaultTestNamespace}};

		    public partial class FirstView : View
		    {
		        [BindableProperty]
		        public partial string FirstText { get; set; }
		    }

		    public partial class SecondView : View
		    {
		        [BindableProperty]
		        public partial string SecondText { get; set; }
		    }
		    """;

		const string expectedFirstGenerated =
			/* language=C#-test */
			//lang=csharp
			$$"""
		    // <auto-generated>
		    // See: CommunityToolkit.Maui.SourceGenerators.Internal.BindablePropertyAttributeSourceGenerator
		    #pragma warning disable
		    #nullable enable
		    namespace {{defaultTestNamespace}};
		    public partial class FirstView
		    {
		        /// <summary>
		        /// Backing BindableProperty for the <see cref = "FirstText"/> property.
		        /// </summary>
		        public static readonly global::Microsoft.Maui.Controls.BindableProperty FirstTextProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("FirstText", typeof(string), typeof(TestNamespace.FirstView), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
		        public partial string FirstText { get => (string)GetValue(FirstTextProperty); set => SetValue(FirstTextProperty, value); }
		    }
		    """;

		const string expectedSecondGenerated =
			/* language=C#-test */
			//lang=csharp
			$$"""
		    // <auto-generated>
		    // See: CommunityToolkit.Maui.SourceGenerators.Internal.BindablePropertyAttributeSourceGenerator
		    #pragma warning disable
		    #nullable enable
		    namespace {{defaultTestNamespace}};
		    public partial class SecondView
		    {
		        /// <summary>
		        /// Backing BindableProperty for the <see cref = "SecondText"/> property.
		        /// </summary>
		        public static readonly global::Microsoft.Maui.Controls.BindableProperty SecondTextProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("SecondText", typeof(string), typeof(TestNamespace.SecondView), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
		        public partial string SecondText { get => (string)GetValue(SecondTextProperty); set => SetValue(SecondTextProperty, value); }
		    }
		    """;


		await VerifySourceGeneratorAsync(source, expectedAttribute, ("FirstView.g.cs", expectedFirstGenerated), ("SecondView.g.cs", expectedSecondGenerated));
	}
}